package McdaMethods;

import java.util.ArrayList;

import utils.DataInitializer;
import utils.MathServices;
import utils.TextPrinter;

public class ElectreIv {

  TextPrinter textPrinter;

  int rowCount;
  int criterionCount;

  String[] criterionNames;
  Double[] weightValues;
  Double[] tresholdsValues;
  Double[][] alternativesArray;
  String[] alternativesNames;

  Double[][] normalizedMatrix;
  Double[] normalizedTresholdsArray;
  Double[][] weightedMatrix;

  Integer[][][] concordanceSets; // zbiory zgodnosci
  Integer[][][] disconcordanceSets;// zbiory niezgosnosci

  Double[][] concordanceMatrix;// macierz zgodnosci
  Double[][] disconcordanceMatrix;// macierz niezgodnosci

  Integer[][] concordanceDominanceMatrix;// macierz dominacji zgodnosci
  Integer[][] disconcordanceDominanceMatrix;// macierz dominacji niezgodnosci

  Integer[][] aggregatedDominanceMatrix;// zagregowana macierz dominacji

  public ElectreIv() {
    textPrinter = TextPrinter.getInstance();
  }

  public void setInputData(String[] alternativesNames, Double[][] alternativesArray, String[] criterionNames,
      Double[] weightValues, Double[] tresholds) {
    this.alternativesNames = alternativesNames;
    this.alternativesArray = alternativesArray;
    this.criterionNames = criterionNames;
    this.weightValues = weightValues;
    this.tresholdsValues = tresholds;
    rowCount = alternativesArray.length;
    criterionCount = criterionNames.length;
  }

  public void work() {
    textPrinter.printLine();
    textPrinter.printLine("*** Starting Electre Iv method ***");
    textPrinter.printLine();
    if (alternativesNames != null && weightValues != null) {

      // Krok 1: obliczenie znormalizowanej macierzy decyzyjnej
      createNormalizedDecisionMatrix();
      // Krok: normalizacja progow veto
      createNormalizedTresholdArray();
      // Krok 2: obliczenie macierzy wazonej znormalizwanej
      createWeightedValuesMatrix();
      // Krok 3: okreslenie zbioru zgodnosci i niezgodnosci
      createConcordanceSets();
      // Krok 4: wyznaczenie macierzy zgodnosci
      createConcordanceMatrix();
      // Krok 5: wyznaczenie macierzy niezgodnosci
      createDisconcordanceMatrix();
      // Krok 6: wyznaczenie macierzy dominacji zgodnosci
      createConcordanceDominanceMatrix();
      // Krok 7: wyznaczenie macierzy dominacji niezgodnosci
      createDisconcordanceDominanceMatrix();
      // Krok 8: wyznaczenie zagregowanej macierzy dominacji
      createAgregatedDominanceMatrix();
      // Krok 9: eliminacja najgorszych wariantow na podstawie
      // zagregowanej macierzy
      getTheBestAlternatives();

    } else {
      textPrinter.printLine("Error: imposible to doMagic(): data have not been set yet");
    }

    textPrinter.printLine();
    textPrinter.printLine("*** Finish Electre I method ***");
    textPrinter.printLine();
  }

  private void createNormalizedTresholdArray() {
    textPrinter.printLine("NORMALIZED TRESHOLD ARRAY:");
    Double[] column;
    normalizedTresholdsArray = new Double[criterionCount];
    for (int i = 0; i < tresholdsValues.length; i++) {
      column = MathServices.getColumn(alternativesArray, i);
      normalizedTresholdsArray[i] = MathServices.normalizacjaArytmetyczna(tresholdsValues[i], column);
    }
    textPrinter.printLine(MathServices.toString(normalizedTresholdsArray));
  }

  private void createNormalizedDecisionMatrix() {
    textPrinter.printLine("NORMALIZED MATRIX: ");
    Double[] column;
    normalizedMatrix = new Double[rowCount][criterionCount];
    for (int i = 0; i < alternativesArray[0].length; i++) {
      column = MathServices.getColumn(alternativesArray, i);
      column = MathServices.normalizacjaArytmetyczna(column, column);
      MathServices.setColumn(normalizedMatrix, column, i);
    }
    textPrinter.printLine(MathServices.toString(normalizedMatrix));
  }

  private void createWeightedValuesMatrix() {
    textPrinter.printLine("WEIGHTED MATRIX:");
    weightedMatrix = new Double[rowCount][criterionCount];
    for (int i = 0; i < rowCount; i++) {
      Double[] weightedRow = MathServices.mnozeniePrzezWagi(MathServices.getRow(normalizedMatrix, i), weightValues);
      MathServices.setRow(weightedMatrix, weightedRow, i);
    }

    textPrinter.printLine(MathServices.toString(weightedMatrix));
  }

  private void createConcordanceSets() {
    concordanceSets = new Integer[rowCount][rowCount][];
    disconcordanceSets = new Integer[rowCount][rowCount][];
    // All possible combinations of 2 different rows:
    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < rowCount; j++) {
        ArrayList<Integer> concordanceSet = new ArrayList<Integer>();
        ArrayList<Integer> disconcordanceSet = new ArrayList<Integer>();
        if (i != j) {// nie porownywac tych samych rzedow
          for (int k = 0; k < criterionCount; k++) {
            if (weightedMatrix[i][k] < (weightedMatrix[j][k] + normalizedTresholdsArray[k])
                && weightedMatrix[i][k] > (weightedMatrix[j][k] - normalizedTresholdsArray[k])) {
              concordanceSet.add(k);
              disconcordanceSet.add(k);
            } else if (weightedMatrix[i][k] > (weightedMatrix[j][k] - normalizedTresholdsArray[k])) {
              concordanceSet.add(k);
            } else {
              disconcordanceSet.add(k);

            }
          }
          concordanceSets[i][j] = concordanceSet.toArray(new Integer[concordanceSet.size()]);
          disconcordanceSets[i][j] = disconcordanceSet.toArray(new Integer[disconcordanceSet.size()]);
        } else {
          concordanceSets[i][j] = new Integer[] {};
          disconcordanceSets[i][j] = new Integer[] {};
        }
      }
    }

    textPrinter.printLine("CONCORDANCE SETS:");
    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < rowCount; j++) {
        textPrinter.print("P(" + i + "," + j + ") = ");
        textPrinter.print(MathServices.toString(concordanceSets[i][j]));
      }
    }

    textPrinter.printLine("DISCONCORDANCE SETS:");
    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < rowCount; j++) {
        textPrinter.print("P(" + i + "," + j + ") = ");
        textPrinter.print(MathServices.toString(disconcordanceSets[i][j]));
      }
    }

  }

  /**
   * Bierze zbior zgodnosci (ktory zawiera indeksy wag), sumuje te wagi i wstawia do odpowiedniej komorki w macierzy
   */
  private void createConcordanceMatrix() {
    concordanceMatrix = new Double[rowCount][rowCount];
    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < rowCount; j++) {
        if (i != j) {
          double weightSum = 0;
          for (int k = 0; k < concordanceSets[i][j].length; k++) {
            weightSum += weightValues[concordanceSets[i][j][k]];
          }
          concordanceMatrix[i][j] = weightSum;
        } else {
          concordanceMatrix[i][j] = null;
        }

      }
    }
    textPrinter.printLine("CONCORDANCE MATRIX:");
    textPrinter.printLine(MathServices.toString(concordanceMatrix, 2));
  }

  /**
   * example: multi7.ppt strona 43
   */
  private void createDisconcordanceMatrix() {
    disconcordanceMatrix = new Double[rowCount][rowCount];
    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < rowCount; j++) {
        if (i != j) {
          // licznik
          Double[] diffNumeratorArray = new Double[disconcordanceSets[i][j].length];
          for (int k = 0; k < disconcordanceSets[i][j].length; k++) {
            int column = disconcordanceSets[i][j][k];
            diffNumeratorArray[k] = Math.abs(weightedMatrix[j][column] - weightedMatrix[i][column]);
          }
          // mianownik
          Double[] diffDenominatorArray = new Double[weightedMatrix[i].length];
          for (int k = 0; k < weightedMatrix[i].length; k++) {
            diffDenominatorArray[k] = Math.abs(weightedMatrix[j][k] - weightedMatrix[i][k]);
          }
          // SUPER WZOR from page 43:
          textPrinter.printLine(i + " " + j);
          if (diffNumeratorArray.length == 0) {// eliminate problem of division by 0
            disconcordanceMatrix[i][j] = 0.0;
          } else if (diffDenominatorArray.length == 0) {
            disconcordanceMatrix[i][j] = 1.0;
          } else {
            disconcordanceMatrix[i][j] = MathServices.max(diffNumeratorArray) / MathServices.max(diffDenominatorArray);
          }

        } else {
          disconcordanceMatrix[i][j] = null;
        }
      }
    }
    textPrinter.printLine("DISCONCORDANCE MATRIX:");
    textPrinter.printLine(MathServices.toString(disconcordanceMatrix));
  }

  /**
   * example: multi7.ppt strona 45
   */
  private void createConcordanceDominanceMatrix() {
    // obliczenie progu zgodnosci
    double progZgodnosci;
    double sum = 0;
    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < rowCount; j++) {
        if (i != j) {
          sum += concordanceMatrix[i][j];
        }
      }
    }
    progZgodnosci = sum / (rowCount * (rowCount - 1));
    textPrinter.printLine("CONCORDANCE TRESHOLD: " + progZgodnosci);
    // wyznaczenie macierzy
    concordanceDominanceMatrix = new Integer[rowCount][rowCount];
    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < rowCount; j++) {
        if (i != j) {
          if (concordanceMatrix[i][j] >= progZgodnosci) {
            concordanceDominanceMatrix[i][j] = 1;
          } else {
            concordanceDominanceMatrix[i][j] = 0;
          }
        } else {
          concordanceDominanceMatrix[i][j] = null;
        }

      }
    }
    textPrinter.printLine("CONCORDANCE DOMINATION MATRIX: ");
    textPrinter.printLine(MathServices.toString(concordanceDominanceMatrix));

  }

  private void createDisconcordanceDominanceMatrix() {
    // obliczenie progu zgodnosci
    double progNiezgodnosci;
    double sum = 0;
    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < rowCount; j++) {
        if (i != j) {
          sum += disconcordanceMatrix[i][j];
        }
      }
    }
    progNiezgodnosci = sum / (rowCount * (rowCount - 1));
    textPrinter.printLine("DISCONCORDANCE TRESHOLD: " + progNiezgodnosci);
    // wyznaczenie macierzy
    disconcordanceDominanceMatrix = new Integer[rowCount][rowCount];
    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < rowCount; j++) {
        if (i != j) {
          if (disconcordanceMatrix[i][j] >= progNiezgodnosci) {
            disconcordanceDominanceMatrix[i][j] = 0;
          } else {
            disconcordanceDominanceMatrix[i][j] = 1;
          }
        } else {
          concordanceDominanceMatrix[i][j] = 0;
        }

      }
    }
    textPrinter.printLine("DISCONCORDANCE DOMINATION MATRIX: ");
    textPrinter.printLine(MathServices.toString(disconcordanceDominanceMatrix));
  }

  private void createAgregatedDominanceMatrix() {
    aggregatedDominanceMatrix = new Integer[rowCount][rowCount];

    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < rowCount; j++) {
        if (i != j) {
          aggregatedDominanceMatrix[i][j] = disconcordanceDominanceMatrix[i][j] * concordanceDominanceMatrix[i][j];
        }
      }
    }
    textPrinter.printLine("AGGREGATED DOMINANCE MATRIX ( F x G ): ");
    textPrinter.printLine(MathServices.toString(aggregatedDominanceMatrix));
  }

  private void getTheBestAlternatives() {
    textPrinter.printLine("------------------------");
    textPrinter.printLine("RESULT: ");
    textPrinter.printLine("THE BEST ALTERNATIVES ARE: ");
    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < rowCount; j++) {
        if (i != j) {
          if (aggregatedDominanceMatrix[i][j] == 1) {
            textPrinter.print(alternativesNames[i] + " -> " + alternativesNames[j]);
            textPrinter.print(", ");
          }
        }
      }
      textPrinter.printLine();
    }
  }

  public static void main(String[] args) {
    TextPrinter textPrinter = TextPrinter.getInstance();
    DataInitializer initializer = new DataInitializer("C://wagi_mini.csv", "C://dane_mini.csv", "C://tresholds_mini.csv");
    ElectreIv electre = new ElectreIv();
    electre.setInputData(initializer.getAlternativesNames(), initializer.getAlternativesArray(), initializer.getCriterionNames(),
        initializer.getWeightValues(), initializer.getTresholdValues());
    textPrinter.printLine("ALTERNATIVES ARRAY:");

    textPrinter.printLine(MathServices.toString(initializer.getAlternativesArray(), 2));
    electre.createNormalizedDecisionMatrix();
    electre.createNormalizedTresholdArray();
    electre.createWeightedValuesMatrix();
    electre.createConcordanceSets();
    electre.createConcordanceMatrix();
    electre.createDisconcordanceMatrix();
    electre.createConcordanceDominanceMatrix();
    electre.createDisconcordanceDominanceMatrix();
    electre.createAgregatedDominanceMatrix();
    electre.getTheBestAlternatives();
  }

}
